// The NSpring Framework for .NET
// Copyright (c) 2003, Jeffrey Varszegi
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without modification, 
// are permitted provided that the following conditions are met:
// 
// - Redistributions of source code must retain the above copyright notice, this 
// list of conditions and the following disclaimer.
// 
// - Redistributions in binary form must reproduce the above copyright notice, this 
// list of conditions and the following disclaimer in the documentation and/or other 
// materials provided with the distribution.
// 
// - Neither the name of the NSpring project nor the names of its contributors may 
// be used to endorse or promote products derived from this software without 
// specific prior written permission from the copyright owner.
// 
// - No product derived from this software may be called "NSpring", nor may 
// "NSpring" appear in the name of such a product, without specific prior written 
// permission from the copyright owner.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
// OF THE POSSIBILITY OF SUCH DAMAGE. 

namespace NSpring.Collections {

/// <summary>
/// <para>
/// A data structure commonly used for routing and buffering work (or any objects) between different
/// threads in the same process; a queue may also be used by a single thread as a way
/// to organize pieces of work in a simple way.
/// </para>
/// <para>
/// Two subclasses, DoubleEndedQueue and PriorityQueue, are provided (see their documentation
/// for more details).  Since they share a common parent class, code receiving objects
/// from a Queue does not need to know the actual implementation, except in the special
/// case where an object must be removed from the front of a double-ended queue.  Either
/// DoubleEndedQueue or PriorityQueue may be used to good effect as a simple single-ended queue
/// by using the methods overridden from this class, Queue.
/// </para>
/// <para>
/// The queue classes are provided in order to remedy the following failings of
/// System.Collections.Queue: <br>
/// - lack of support for double-ended and priority queues, two commonly-needed types
/// in programming many different types of systems <br>
/// - lack of support for waiting with timeout in case of nothing being immediately
/// available in the queue; this leads to polling, with unavoidable loss averaging
/// half the polling interval <br>
/// - slow performance when synchronized <br>
/// - poor naming (method names Add and Remove are more easily and intuitively understood than 
/// Enqueue and Dequeue, leading to more readable code)<br>
/// </para>
/// <para>
/// All NSpring.Collections.Queue instances are synchronized.  This is in recognition
/// of the main use of such in-memory structures in actual practice, which is to route
/// objects between threads in the same process.  Multiple threads requires synchronization,
/// and the approach in the System.Collections namespace (wrapping an unsynchronized object in 
/// a synchronized wrapper) unnecessarily slows performance in this situation due to 
/// unnecessary method calls.
/// </para>
/// </summary>
public abstract class Queue {
    private static object[] EMPTY_OBJECT_ARRAY      = {};
    
    protected bool isOpen                           = true;

    protected int count                             = 0;
    
    protected bool isNullAllowed                    = false;
    
    protected object monitor                        = new object();    
    
    /// <summary>
    /// Adds an element to the queue.  Implemented in subclasses
    /// </summary>
    /// <param name="_object">The element to add</param>
    public abstract void Add(object _object);

    /// <summary>
    /// Gets the next object from the queue without removing it.  Implemented in subclasses
    /// </summary>
    public abstract object Peek();

    /// <summary>
    /// Gets the next object from the queue without removing it.  If no object is available,
    /// waits up to the specified number of milliseconds, then returns null if no object is
    /// available.
    /// Implemented in subclasses
    /// </summary>
    public abstract object Peek(int _millisecondsTimeout);

    /// <summary>
    /// Removes the next object from the queue.  Implemented in subclasses
    /// </summary>
    public abstract object Remove();

    /// <summary>
    /// Removes the next object from the queue.  If no object is available,
    /// waits up to the specified number of milliseconds, then returns null if no object is
    /// available.
    /// Implemented in subclasses
    /// </summary>
    public abstract object Remove(int _millisecondsTimeout);
    
    /// <summary>
    /// Removes all objects from the queue and returns them in ascending order, with 
    /// the element at index zero being the one "next in line"
    /// </summary>
    /// <returns></returns>
    public virtual object[] RemoveAll() {
        lock (monitor) {
            if (count == 0) {
                return EMPTY_OBJECT_ARRAY;
            }
            else {
                object[] elements = new object[count];
                for(int x = 0; x < elements.Length; x++) {
                    elements[x] = Remove();
                }
                return elements;
            }
        }
    }
    
    /// <summary>
    /// Releases waiting threads, immediately returning null values to them, but
    /// leaves the queue open
    /// </summary>
    public void ReleaseWaitingThreads() {
        lock (monitor) {
            bool open = isOpen;
            Close();
            isOpen = open;
        }
    }
    
    /// <summary>
    /// Allows the queue to accept input.  Each Queue instance is open when
    /// it is created.
    /// </summary>
    public virtual void Open() {
        lock (monitor) {
            isOpen = true;
        }
    }
    
    /// <summary>
    /// Renders the queue incapable of accepting further input; also releases all
    /// waiting threads, immediately returning them null values
    /// </summary>
    public abstract void Close();
    
    /// <summary>
    /// Indicates whether the queue can accept further input
    /// </summary>
    public bool IsOpen {
        get {
            lock (monitor) {
                return isOpen;
            }
        }
    }
    
    /// <summary>
    /// Indicates the number of elements currently held in the queue
    /// </summary>
    public int Count {
        get {
            lock (monitor) {
                return count;
            }
        }
    }
    
    /// <summary>
    /// Clears all elements from the queue
    /// </summary>
    public abstract void Clear();
    
    /// <summary>
    /// Gets and sets whether null values can be added to the queue
    /// </summary>
    public bool IsNullAllowed {
        get {
            lock (monitor) {
                return isNullAllowed;
            }
        }
        set {
            lock (monitor) {
                isNullAllowed = value;
            }
        }
    }


}

}
